package com.jctrip.hotel.source.service.hmein.impl;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jctrip.hotel.domain.dao.HtSourceInfoDao;
import com.jctrip.hotel.domain.entity.HtSourceInfo;
import com.jctrip.hotel.source.model.hmein.req.*;
import com.jctrip.hotel.source.model.hmein.resp.*;
import com.jctrip.hotel.source.service.HotelCommonTaskService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.jctrip.hotel.common.constant.RedisKeyConstant;
import com.jctrip.hotel.common.enums.OrderStatusEnum;
import com.jctrip.hotel.common.enums.SourceEnum;
import com.jctrip.hotel.common.util.JsonUtil;
import com.jctrip.hotel.common.util.RedisUtil;
import com.jctrip.hotel.source.client.HmeinClient;
import com.jctrip.hotel.source.model.OrderDataResult;
import com.jctrip.hotel.source.model.PriceCheckParam;
import com.jctrip.hotel.source.model.hmein.resp.ChannelRmtypeResult.ChannelRmtypes;
import com.jctrip.hotel.source.model.hmein.resp.CheckAvailableResult.DailyRate;
import com.jctrip.hotel.source.model.hmein.resp.CityListResult.CityList;
import com.jctrip.hotel.source.model.hmein.resp.HotelFacilitiesResult.HotelFacilitiesList;
import com.jctrip.hotel.source.model.hmein.resp.HotelInfosResult.HotelInfos;
import com.jctrip.hotel.source.model.hmein.resp.HotelRmMcResult.HotelRmMc;
import com.jctrip.hotel.source.model.wehotel.req.OrderData;
import com.jctrip.hotel.source.service.hmein.HmeinService;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author henry
 * @Date 2023/6/3 17:23
 * @Description
 */
@Service
@Slf4j
public class HmeinServiceImpl implements HmeinService {

    @Resource
    private HmeinClient hmeinClient;
    @Resource
    private RedisUtil redisUtil;

    @Resource
    private HtSourceInfoDao htSourceInfoDao;

    @Autowired
    private HotelCommonTaskService hotelCommonTaskService;

    private BaseReq buildBaseReq(String appId) {
        return BaseReq.builder().Terminal_OprId("zjjc").Terminal_License(appId)
                .Terminal_Seq(String.valueOf(System.currentTimeMillis())).build();
    }

    @Override
    public String parseHotelId(String innId) {
        if (StringUtils.isBlank(innId)) {
            return null;
        }
        if (innId.startsWith(SourceEnum.HMEIN.getCode())) {
            return innId.substring(SourceEnum.HMEIN.getCode().length());
        }
        return innId;
    }

    @Override
    public String parseInnId(String hotelId) {
        if (null == hotelId) {
            return null;
        }
        return SourceEnum.HMEIN.getCode() + hotelId;
    }


    @Override
    public List<CityList> getCityData(String appId) {
        String tokenCacheKey = String.format("%s_%s", RedisKeyConstant.HOTEL_MAIN_HMEIN_CITY_CACHE, appId);
        long lockExpiry = 90 * 60;// 单位：秒 1.5小时


        CityListResult cacheResult = (CityListResult) redisUtil.get(tokenCacheKey);
        if (cacheResult != null) {
            return cacheResult.getCityList();
        }

        CityListResult result = hmeinClient.getCityListData(buildBaseReq(appId));
        if (result.getResCode() != 0) {
            throw new RuntimeException(result.getResDesc());
        }
        redisUtil.set(tokenCacheKey, result, lockExpiry);
        return result.getCityList();
    }

    @Override
    public List<String> getHotelIds(String appId) {
        HotelIdListReq req = new HotelIdListReq();
        req.setTerminal_License(appId);
        req.setTerminal_OprId("zjjc");
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        req.setType("T01");
        ChannelRmtypeResult economyHotel = hmeinClient.getHotelIdList(req);
        if (economyHotel.getFail()) {
            throw new RuntimeException(economyHotel.getResDesc());
        }
        req.setType("T02");
        ChannelRmtypeResult luxuryHotel = hmeinClient.getHotelIdList(req);
        if (luxuryHotel.getFail()) {
            throw new RuntimeException(luxuryHotel.getResDesc());
        }

        List<String> hotelIds = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(economyHotel.getChannelRmtypes())) {
            hotelIds.addAll(economyHotel.getChannelRmtypes().stream().map(ChannelRmtypes::getHotelCd).distinct()
                    .collect(Collectors.toList()));
        }
        if (CollectionUtils.isNotEmpty(luxuryHotel.getChannelRmtypes())) {
            hotelIds.addAll(luxuryHotel.getChannelRmtypes().stream().map(ChannelRmtypes::getHotelCd).distinct()
                    .collect(Collectors.toList()));
        }
        log.info("getHotelIds size : {}  ids :{}", hotelIds.size(), JsonUtil.toString(hotelIds));
        // 去重后返回
        return hotelIds.stream().distinct().collect(Collectors.toList());
    }

    @Override
    public HotelInfos getHotelInfo(String appId, String hotelId) {
        HotelInfoReq req = new HotelInfoReq();
        req.setHotelCd(hotelId);
        req.setTerminal_OprId("zjjc");
        req.setTerminal_License(appId);
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        HotelInfosResult hotelInfo = hmeinClient.getHotelInfo(req);
        if (hotelInfo.getFail()) {
            throw new RuntimeException(hotelInfo.getResDesc());
        }
        return hotelInfo.getHotelInfos().get(0);
    }

    @Override
    public List<GetHmeinBrandListResult.BrandList> getBrand(String appId) {
        GetHmeinBrandListReq req = new GetHmeinBrandListReq();
        req.setTerminal_OprId("zjjc");
        req.setTerminal_License(appId);
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        GetHmeinBrandListResult brandList = hmeinClient.getBrandList(req);
        if (brandList.getFail()) {
            throw new RuntimeException(brandList.getResDesc());
        }
        return brandList.getBrandList();
    }


    @Override
    public List<HotelRmMc> getRoomType(String appId, String hotelId) {
        RoomTypeReq req = new RoomTypeReq();
        req.setHotelCd(hotelId);
        req.setTerminal_OprId("zjjc");
        req.setTerminal_License(appId);
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        HotelRmMcResult roomType = hmeinClient.getRoomType(req);
        if (roomType.getFail()) {
            throw new RuntimeException(roomType.getResDesc());
        }
        return roomType.getHotelRmMc();
    }

    @Override
    public List<String> getHotelPic(String appId, String hotelId) {
        HotelPicReq req = new HotelPicReq();
        req.setHotelCd(hotelId);
        req.setTerminal_OprId("zjjc");
        req.setTerminal_License(appId);
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        HotelPicResult hotelPicUrl = hmeinClient.queryHotelPicUrl(req);
        if (hotelPicUrl.getFail()) {
            throw new RuntimeException(hotelPicUrl.getResDesc());
        }
        return hotelPicUrl.getPicUrls();
    }

    @Override
    public List<HotelFacilitiesList> getHotelFacilitiesListData(String appId, String hotelId) {
        HotelFacilitiesListDataReq req = new HotelFacilitiesListDataReq();
        req.setHotelCd(hotelId);
        req.setTerminal_OprId("zjjc");
        req.setTerminal_License(appId);
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));

        HotelFacilitiesResult hotelFacilitiesListData = hmeinClient.getHotelFacilitiesListData(req);
        if (hotelFacilitiesListData.getFail()) {
            throw new RuntimeException(hotelFacilitiesListData.getResDesc());
        }
        return hotelFacilitiesListData.getHotelFacilitiesList();
    }

    @Override
    public RoomStatusResult getRoomStatus(String appId, String memberNo, String hotelId, String roomTypeId,
                                          String start, String end) {
        RoomStatusReq req = new RoomStatusReq();
        req.setHotelCd(hotelId);
        req.setArrDate(start);
        req.setDepDate(end);
        req.setResvType("CSRV");
        req.setMembershipType("CY");
        req.setRoomNum(1);
        req.setAdults(1);
        req.setMemberNo(memberNo);
        req.setAllowLoadPromotion(true);
        req.setOnlyShowEnabledPromotion(false);
        if (StringUtils.isNotBlank(roomTypeId)) {
            req.setRmTypeCds(roomTypeId);
        }
        req.setTerminal_License(appId);
        req.setTerminal_OprId("zjjc");
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        RoomStatusResult roomStatus = hmeinClient.getRoomStatus(req);
        return roomStatus;
    }

    @Override
    public CheckAvailableResult checkAvailability(CheckAvailableReq req) {
        req.setTerminal_OprId("zjjc");
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        // todo
        req.setServiceFlag("1");
        req.setResvType("CSRV");
        req.setMemberType("cy");
        // todo
        req.setLicenseFlag("1");
        CheckAvailableResult checkAvailableResult = hmeinClient.checkAvailable(req);
        if (checkAvailableResult.getFail()) {
            throw new RuntimeException(checkAvailableResult.getResDesc());
        }
        return checkAvailableResult;
    }

    @Override
    public CreateOrderResult createOrder(CreateOrderReq req) {
        req.setTerminal_OprId("zjjc");
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        req.setServiceFlag("1");
        req.setResvType("CSRV");
        req.setMemberType("cy");
        if (StringUtils.isBlank(req.getLicenseFlag())) {
            req.setLicenseFlag("1");
        }
        req.setPayCd("12");
        req.setMobileTel("057122934358");
        req.setNote1("预定联系人仅限重要事件沟通，日常确认请勿联系");
        req.getGustInfos().forEach(gustInfo -> {
            gustInfo.setPhone("057122934358");
        });
        CreateOrderResult order = hmeinClient.createOrder(req);
        if (order.getFail()) {
            hotelCommonTaskService.insertOrderErrorLog(req.getOutOrders().get(0).getOutOrderNo(),
                    JsonUtil.toString(req), order.getResDesc());
            throw new RuntimeException(order.getResDesc());
        }
        return order;
    }

    @Override
    public OrderDataResult postOrder(CreateOrderReq req, PriceCheckParam priceCheck, String sourceAppId) {
        // todo 先试单 试单转成调用房态
//        CheckAvailableReq checkAvailableReq = new CheckAvailableReq();
//        checkAvailableReq.setHotelCd(req.getHotelCd());
//        checkAvailableReq.setRateCode(req.getRateCode());
//        checkAvailableReq.setArrDate(req.getArrDate());
//        checkAvailableReq.setDepDate(req.getDepDate());
//        checkAvailableReq.setCRSRmType(req.getCRSRmType());
//        checkAvailableReq.setRmNum(req.getRmNum());
//        checkAvailableReq.setMemberNo(req.getMemberNo());
//        checkAvailableReq.setTerminal_License(req.getTerminal_License());
//        checkAvailableReq.setTrustMemberNo(req.getTrustMemberNo());
//        CheckAvailableResult checkAvailableResult = checkAvailability(checkAvailableReq);
//        List<DailyRate> dailyRate = checkAvailableResult.getDailyRate();
//        for (DailyRate rate : dailyRate) {
//            CreateOrderReq.DailyRate createDailyRate = new CreateOrderReq.DailyRate();
//            createDailyRate.setRate(rate.getPrice());
//            createDailyRate.setDate(rate.getStDate());
//            list.add(createDailyRate);
//            markupPayMoney = markupPayMoney.add(rate.getPrice());
//            raisedTotalPrice = raisedTotalPrice.add(priceCheck.getRaiseFunc().apply(rate.getPrice()));
//        }
        RoomStatusResult roomStatus =
                getRoomStatus(sourceAppId, StringUtils.isNotBlank(req.getTrustMemberNo()) ? req.getTrustMemberNo() : req.getMemberNo(), req.getHotelCd(), req.getCRSRmType(),
                        DateFormatUtils.format(req.getArrDate(), "yyyy-MM-dd"),
                        DateFormatUtils.format(req.getDepDate(), "yyyy-MM-dd"));
        if (roomStatus.getFail()) {
            throw new RuntimeException("房态信息为空");
        }
        String rateCode = req.getRateCode();
        if (StringUtils.isBlank(rateCode)) {
            throw new RuntimeException("账户价格码配置为空");
        }
        // 先看这里
        List<CreateOrderReq.DailyRate> list = new ArrayList<>();
        BigDecimal markupPayMoney = BigDecimal.ZERO;
        BigDecimal raisedTotalPrice = BigDecimal.valueOf(0);


        List<RoomStatusResult.Products> productsList = roomStatus.getProducts();
        for (RoomStatusResult.Products products : productsList) {
            RoomStatusResult.RoomRates roomRate =
                    products.getRoomRates().stream()
                            .filter(roomRates -> roomRates.getRateCode().equals(rateCode))
                            .findFirst().orElse(null);
            if (Objects.isNull(roomRate)) {
                continue;
            }
            List<RoomStatusResult.RoomRateDetailDailys> rateDetailDailys = Optional.ofNullable(roomRate.getRoomRateDetailDailys()).orElse(new ArrayList<>());
            for (RoomStatusResult.RoomRateDetailDailys roomRateDetailDailys : rateDetailDailys) {
                CreateOrderReq.DailyRate createDailyRate = new CreateOrderReq.DailyRate();
                createDailyRate.setRate(roomRateDetailDailys.getPrs1());
                createDailyRate.setDate(roomRateDetailDailys.getStDate());
                list.add(createDailyRate);
                markupPayMoney = markupPayMoney.add(roomRateDetailDailys.getPrs1());
                raisedTotalPrice = raisedTotalPrice.add(priceCheck.getRaiseFunc().apply(roomRateDetailDailys.getPrs1()));
            }
        }
        // todo 临时不价格还原
//        if (priceCheck.getTotalRate().compareTo(raisedTotalPrice.multiply(BigDecimal.valueOf(req.getRmNum()))
//                .subtract(priceCheck.getTolerancePrice())) < 0) {
//            log.error("订单销售价不能低于采购总价，订单销售价={}，采购总价={}, 容差金额={}",
//                    priceCheck.getTotalRate(), raisedTotalPrice, priceCheck.getTolerancePrice());
//            throw new RuntimeException("订单销售价不能低于采购总价");
//        }
        if (priceCheck.getTotalRate().compareTo(markupPayMoney.multiply(BigDecimal.valueOf(req.getRmNum()))
                .subtract(priceCheck.getTolerancePrice())) < 0) {
            String error = String.format("订单销售价不能低于采购总价，订单销售价=%s，采购总价=%s, 容差金额=%s, 目前差额=%s",
                    priceCheck.getTotalRate(),
                    markupPayMoney.multiply(BigDecimal.valueOf(req.getRmNum())),
                    priceCheck.getTolerancePrice(),
                    markupPayMoney.multiply(BigDecimal.valueOf(req.getRmNum())).subtract(priceCheck.getTotalRate()));
            hotelCommonTaskService.insertOrderErrorLog(req.getOutOrders().get(0).getOutOrderNo(),
                    JsonUtil.toString(req), error);
            log.error("订单销售价不能低于采购总价，订单销售价={}，采购总价={}, 容差金额={}, 目前差额={}",
                    priceCheck.getTotalRate(), markupPayMoney.multiply(BigDecimal.valueOf(req.getRmNum())),
                    priceCheck.getTolerancePrice(), markupPayMoney.multiply(BigDecimal.valueOf(req.getRmNum())).subtract(priceCheck.getTotalRate()));
            throw new RuntimeException("订单销售价不能低于采购总价");
        }
        req.setDailyRates(list);
        req.setMarkupPayMoney(markupPayMoney.multiply(BigDecimal.valueOf(req.getRmNum())));
        req.setMustPayMoney(markupPayMoney.multiply(BigDecimal.valueOf(req.getRmNum())));
        req.setEarliestTime("14:00");
        req.setLastTime("23:59");
        req.getGustInfos().forEach(gustInfo -> {
            gustInfo.setEarliestTime("14:00");
            gustInfo.setLastTime("23:59");
            gustInfo.setMemberNo(req.getMemberNo());
            gustInfo.setMemberType("cy");
        });
        CreateOrderResult order = createOrder(req);
        return OrderDataResult.builder().orderCode(order.getOrderNo())
                .externalId(req.getOutOrders().get(0).getOutOrderNo()).totalRate(markupPayMoney.multiply(BigDecimal.valueOf(req.getRmNum()))).orderState(
                        OrderStatusEnum.BOOK_SUCCESS.getCode()).requestJsonStr(JsonUtil.toString(req))
                .responseJsonStr(JsonUtil.toString(order)).build();
    }

    @Override
    public QueryOrderStatusResult queryOrderStatus(String appId, QueryOrderStatusReq req) {
        req.setTerminal_OprId("zjjc");
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        req.setTerminal_License(appId);
        QueryOrderStatusResult queryOrderStatusResp = hmeinClient.queryOrderStatus(req);
        if (queryOrderStatusResp.getFail()) {
            throw new RuntimeException(queryOrderStatusResp.getResDesc());
        }
        return queryOrderStatusResp;
    }

    @Override
    public QueryOrderStatusResult queryDirectOrderStatus(String sourceId, QueryOrderStatusReq req) {
        if (StringUtils.isBlank(req.getOrderNo()) && StringUtils.isBlank(req.getOutOrderNo())) {
            throw new RuntimeException("订单号为空");
        }
        HtSourceInfo htSourceInfo =
                htSourceInfoDao.selectOne(new LambdaQueryWrapper<HtSourceInfo>().eq(HtSourceInfo::getSourceId,
                        sourceId));
        if (htSourceInfo == null) {
            throw new RuntimeException("供应商账户为空");
        }
        String appId = htSourceInfo.getSourceAppId();

        req.setTerminal_OprId("zjjc");
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        req.setTerminal_License(appId);
        QueryOrderStatusResult queryOrderStatusResp = hmeinClient.queryOrderStatus(req);
        if (queryOrderStatusResp.getFail()) {
            throw new RuntimeException(queryOrderStatusResp.getResDesc());
        }
        return queryOrderStatusResp;
    }

    @Override
    public OrderData queryOrderData(String appId, String orderNo, String outOrderNo) {
        QueryOrderStatusReq req = new QueryOrderStatusReq();
        if (StringUtils.isNotBlank(orderNo)) {
            req.setOrderNo(orderNo);
        }
        if (StringUtils.isNotBlank(outOrderNo)) {
            req.setOutOrderNo(outOrderNo);
        }
        QueryOrderStatusResult queryOrderStatusResp = queryOrderStatus(appId, req);
        OrderData orderData = new OrderData();
        orderData.setOrderCode(queryOrderStatusResp.getOrderNo());
        //          1-预定     2-取消   3-夜审取消（即noshow） 4-入住   5-离店
        Integer orderState = null;
        switch (queryOrderStatusResp.getOrderStatus()) {
            case "R":
                orderState = 1;
                break;
            case "C":
                orderState = 2;
                break;
            case "NW":
                orderState = 3;
                break;
            case "I":
                orderState = 4;
                break;
            case "O":
            case "QK":
                orderState = 5;
                break;
            default:
        }
        orderData.setOrderState(orderState);
        return orderData;
    }

    @Override
    public Boolean cancelOrder(String appId, String orderNo) {
        CancelOrderReq req = new CancelOrderReq();
        req.setOrderNo(orderNo);
        req.setTerminal_OprId("zjjc");
        req.setTerminal_License(appId);
        req.setTerminal_Seq(String.valueOf(System.currentTimeMillis()));
        CancelOrderResult cancelOrderResp = hmeinClient.cancelOrder(req);
        if (cancelOrderResp.getFail()) {
            throw new RuntimeException(cancelOrderResp.getResDesc());
        }
        return Boolean.TRUE;
    }

    @Override
    public List<RoomStatusResult.RoomRateDetailDailys> getRetailPriceList(String appId, String hotelId, String roomType, String memberNo, String start, String end) {
        // 查询房态
        RoomStatusResult roomStatus = getRoomStatus(appId, memberNo, hotelId, roomType, start, end);
        if (roomStatus == null && roomStatus.getFail()) {
            throw new RuntimeException("查询房态信息为空");
        }

        RoomStatusResult.Products products = roomStatus.getProducts().get(0);
        // 门市价房态
        List<RoomStatusResult.RoomRateDetailDailys> retailPriceDailys =
                products.getRoomRates().stream()
                        .filter(roomRates -> roomRates.getRateCode().equals("BAR"))
                        .findFirst().map(RoomStatusResult.RoomRates::getRoomRateDetailDailys).orElse(new ArrayList<>());
        return retailPriceDailys;
    }

    @Override
    public String getTime(String time) {
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            Date timeDate = simpleDateFormat.parse(time);
            return simpleDateFormat.format(timeDate);
        } catch (Exception e) {
            throw new RuntimeException("时间格式化失败");
        }
    }
}
